package com.gusto.engine.beans.model.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Configurable;

import com.gusto.engine.beans.exceptions.PropertyAccessException;
import com.gusto.engine.beans.exceptions.ResCreationException;
import com.gusto.engine.beans.model.RepositoryRes;
import com.gusto.engine.beans.model.Res;
import com.hp.hpl.jena.datatypes.xsd.XSDDateTime;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;

/**
 * <p>The SemanticModel Resource {@link SemanticModelRes} defines an Ontology term as a resource.<br/>
 * It defines the properties to access to resource properties.<br/>
 * The implementation relies on Jena semantic API 
 * so you have to pass a {@code com.hp.hpl.jena.rdf.model.Resource} object 
 * to create the resource.</p>
 * 
 * @author amokrane.belloui@gmail.com
 * 
 */
@Configurable
public class SemanticModelRes implements RepositoryRes {
	 
	private Logger log = Logger.getLogger(getClass());
	
	private Resource theResource;
	
	public SemanticModelRes() {
		super();
	}
	
	private SemanticModelRes(Resource theResource) {
		this();
		this.theResource = theResource;
		log.debug("Resource " + theResource + " created");
	}
	
	public String toString() {
		return theResource.getURI();
	}
	
	public boolean equals(Object anObject) {
		if (anObject instanceof SemanticModelRes) {
			SemanticModelRes res = (SemanticModelRes)anObject;
			if (theResource.getURI() != null && res.getResource().getURI() != null) {
				return theResource.getURI().equals(res.getResource().getURI());
			}
		}
		return false;
	}
	
	/**
	 * Factory method for creating a resource.
	 * @param theResource - the Jena Resource object that you wan't to use as a {@link Comparable} Resource {@link Res}
	 * @return
	 */
	public static Res createResource(Resource theResource) throws ResCreationException {
		SemanticModelRes res = new SemanticModelRes(theResource);
		// Do a check (of something)
		return res;
	}
	
	public Resource getResource() {
		return theResource;
	}
	
	private Object processSpecialLiteralTypes(Object val) {
		if (val instanceof XSDDateTime) {
			return ((XSDDateTime)val).asCalendar().getTime();
		}
		
		return val;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public Res getResourceProperty(String property) throws PropertyAccessException {
		Property prop = theResource.getModel().getProperty(property);
		StmtIterator stmtIt = theResource.listProperties(prop);
		if (stmtIt.hasNext()) {
			try {
				Resource resource = stmtIt.nextStatement().getResource();
				return new SemanticModelRes(resource);
			}
			catch (Exception e) {
				throw new PropertyAccessException("Cannot access resource property " + property + " on object " + theResource.getURI(), e);
			}
		}
		else {
			return null;
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Res> getResourceSetProperty(String property) throws PropertyAccessException {
		Property prop = theResource.getModel().getProperty(property);
		StmtIterator stmtIt = theResource.listProperties(prop);
		
		List<Res> result = new ArrayList<Res>();
		
		while (stmtIt.hasNext()) {
			try {
				Statement stmt = stmtIt.nextStatement();
				Resource r = (Resource) stmt.getObject();
				result.add(new SemanticModelRes(r));
			} 
			catch (Exception e) {
				throw new PropertyAccessException("Cannot access resource set property " + property + " on object " + theResource.getURI(), e);
			}
		}
		return result;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public Object getValueProperty(String property) throws PropertyAccessException {
		Property prop = theResource.getModel().getProperty(property);
		StmtIterator stmtIt = theResource.listProperties(prop);
		
		if (stmtIt.hasNext()) {
			try {
				Literal literal = stmtIt.nextStatement().getLiteral();
				return processSpecialLiteralTypes(literal.getValue());
			} 
			catch (Exception e) {
				throw new PropertyAccessException("Cannot access value property " + property + " on object " + theResource.getURI(), e);
			}
		}
		else {
			return null;
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Object> getValueSetProperty(String property) throws PropertyAccessException {
		Property prop = theResource.getModel().getProperty(property);
		StmtIterator stmtIt = theResource.listProperties(prop);
		
		List<Object> result = new ArrayList<Object>();
		
		while (stmtIt.hasNext()) {
			try {
				Statement stmt = stmtIt.nextStatement();
				Literal l = (Literal) stmt.getObject();
				result.add(processSpecialLiteralTypes(l.getValue()));
			} 
			catch (Exception e) {
				throw new PropertyAccessException("Cannot access value set property " + property + " on object " + theResource.getURI(), e);
			}
		}
		return result;
	}
	
}
